using System.Collections;
using UnityEngine;
using VoxelEngine.Utils;

namespace VoxelEngine
{
	[CreateAssetMenu(menuName = "Voxel/BlockData")]
	public class BlockBase : ScriptableObject
	{
		[SerializeField]
		private Vector2 _textureBottomUV;

		[SerializeField]
		private Vector2 _textureTopUV;

		[SerializeField]
		private Vector2 _textureSideUV;

		[SerializeField]
		private Vector2 _textureUV;

		[SerializeField]
		private ParticleSystem _createParticle;

		[SerializeField]
		private ParticleSystem _destroyParticle;

		[SerializeField]
		private float _destroyParticleFrame;

		[SerializeField]
		private GameObject _model;

		private const int MaxWaterSteps = 3;

		public Vector2 TextureBottomUV()
		{
			return _textureBottomUV;
		}

		public Vector2 TextureTopUV()
		{
			return _textureTopUV;
		}

		public Vector2 TextureSideUV()
		{
			return _textureSideUV;
		}

		public Vector2 TextureUV()
		{
			return _textureUV;
		}

		public GameObject Model()
		{
			return _model;
		}

		public ParticleSystem DestroyParticle()
		{
			return _destroyParticle;
		}

		public bool Destroy(Block block, Transform worldTransform, byte light = 51)
		{
			if (!block.IsSolid() && block.Type != BlockType.Water)
			{
				return false;
			}
			ParticleSystem particleSystem = SpawnBlockParticle(block, light, _destroyParticle, worldTransform, 0.5f);
			if (particleSystem != null)
			{
                ParticleSystem.TextureSheetAnimationModule textureSheetAnimation = particleSystem.textureSheetAnimation;
                    textureSheetAnimation.startFrame = _destroyParticleFrame / 32f;
			}
			if (block.Chunk != null)
			{
				block.Chunk.SetDirty();
			}
			block.Type = BlockType.Air;
			Block block2 = IsThereWaterBlockNeighbor(block);
			if (block2 != null)
			{
				CoroutineQueueManager.Instance.WaterFlood.Run(FloodBlock(block2, 3));
			}
			return true;
		}

		public bool Create(Block block, BlockType type, Transform worldTransform, BlockVolume volume = BlockVolume.Full)
		{
			if (block._wy >= 8 * World.VisibleY - 1)
			{
				return false;
			}
			if (block.Chunk == null)
			{
				return false;
			}
			BlockVolume blockVolume = block.Volume();
			if (block.IsSolid() && blockVolume == BlockVolume.Full)
			{
				return false;
			}
			if (blockVolume != 0)
			{
				type = BlockTypes.Instance.GetMixedBlock(block.Type, type);
			}
			SpawnBlockParticle(block, 0, _createParticle, worldTransform);
			block.Type = type;
			block.Chunk.SetDirty();
			if (type == BlockType.Water && !UpdateLavaToHardStoneIfNeeded(block))
			{
				CoroutineQueueManager.Instance.WaterFlood.Run(FloodBlock(block, 3));
			}
			return true;
		}

		private bool SetBlockToWater(Block block)
		{
			block.Type = BlockType.Water;
			ChunkRenderer.UpdateHeightColumn(block.Chunk.WorldRef, block._wx, block._wz);
			return !UpdateLavaToHardStoneIfNeeded(block);
		}

		private bool UpdateLavaToHardStoneIfNeeded(Block block)
		{
			Block block2 = block.Chunk.WorldRef[block._wx, block._wy - 1, block._wz];
			if (block2.Type == BlockType.Lava)
			{
				block2.Type = BlockType.HardStone;
				SpawnBlockParticle(block, 50, block.DestroyParticle(), block.Chunk.WorldRef.WorldTransform);
				block.Type = BlockType.Air;
				block.Chunk.SetDirty();
				block2.Chunk.SetDirty();
				return true;
			}
			return false;
		}

		private IEnumerator FloodBlock(Block block, int steps, bool applyGravity = true)
		{
			Block bottomNeighbor = block.Chunk.WorldRef[block._wx, block._wy - 1, block._wz];
			if (bottomNeighbor != null && !bottomNeighbor.IsSolid())
			{
				yield return new WaitForSeconds(0.25f);
				block.Type = BlockType.Air;
				block.Chunk.SetDirty();
				if (bottomNeighbor.Type != BlockType.Water)
				{
					DirtyChunkIfWater(block.Chunk.WorldRef[block._wx + 1, block._wy, block._wz]);
					DirtyChunkIfWater(block.Chunk.WorldRef[block._wx - 1, block._wy, block._wz]);
					DirtyChunkIfWater(block.Chunk.WorldRef[block._wx, block._wy, block._wz + 1]);
					DirtyChunkIfWater(block.Chunk.WorldRef[block._wx, block._wy, block._wz - 1]);
					SetBlockToWater(bottomNeighbor);
					bottomNeighbor.Chunk.SetDirty();
					bottomNeighbor.Chunk.ChunkObject.StartCoroutine(FloodBlock(bottomNeighbor, steps));
				}
			}
			else if (steps > 0)
			{
				steps--;
				yield return new WaitForSeconds(0.5f);
				if (HorizontalFloodBlock(block, -1, 0, steps))
				{
					yield return new WaitForSeconds(0.5f);
				}
				if (HorizontalFloodBlock(block, 1, 0, steps))
				{
					yield return new WaitForSeconds(0.5f);
				}
				if (HorizontalFloodBlock(block, 0, -1, steps))
				{
					yield return new WaitForSeconds(0.5f);
				}
				if (HorizontalFloodBlock(block, 0, 1, steps))
				{
					yield return new WaitForSeconds(0.5f);
				}
			}
		}

		private void DirtyChunkIfWater(Block block)
		{
			if (block.Type == BlockType.Water)
			{
				block.Chunk.SetDirty();
			}
		}

		private bool HorizontalFloodBlock(Block block, int offSetX, int offSetZ, int steps)
		{
			Block block2 = block.Chunk.WorldRef[block._wx + offSetX, block._wy, block._wz + offSetZ];
			if (block2 != null && !block2.IsSolid() && block2.Type != BlockType.Water)
			{
				Block block3 = block.Chunk.WorldRef[block._wx + offSetX, block._wy - 1, block._wz + offSetZ];
				if (block3 != null && block3.Chunk != null)
				{
					if (block3.IsSolid())
					{
						if (SetBlockToWater(block2))
						{
							block2.Chunk.SetDirty();
							CoroutineQueueManager.Instance.WaterFlood.Run(FloodBlock(block2, steps));
						}
					}
					else
					{
						SetBlockToWater(block3);
						block3.Chunk.SetDirty();
						CoroutineQueueManager.Instance.WaterFlood.Run(FloodBlock(block3, steps));
					}
					return true;
				}
			}
			return false;
		}

		private Block IsThereWaterBlockNeighbor(Block block)
		{
			Block block2 = block.Chunk.WorldRef[block._wx, block._wy + 1, block._wz];
			if (block2.Type == BlockType.Water)
			{
				return block2;
			}
			block2 = block.Chunk.WorldRef[block._wx - 1, block._wy, block._wz];
			if (block2.Type == BlockType.Water)
			{
				return block2;
			}
			block2 = block.Chunk.WorldRef[block._wx + 1, block._wy, block._wz];
			if (block2.Type == BlockType.Water)
			{
				return block2;
			}
			block2 = block.Chunk.WorldRef[block._wx, block._wy, block._wz - 1];
			if (block2.Type == BlockType.Water)
			{
				return block2;
			}
			block2 = block.Chunk.WorldRef[block._wx, block._wy, block._wz + 1];
			if (block2.Type == BlockType.Water)
			{
				return block2;
			}
			return null;
		}

		public bool Spawn3DModelIfNeeded(Block block)
		{
			if (_model != null)
			{
				GameObject gameObject = DynamicObjectPool.Instance.Create(_model);
				if (gameObject != null)
				{
					Vector3 position = new Vector3((float)block._wx + 0.5f, (float)block._wy + 0.5f, (float)block._wz + 0.5f);
					gameObject.transform.position = position;
					return true;
				}
			}
			return false;
		}

		private ParticleSystem SpawnBlockParticle(Block block, byte light, ParticleSystem particleSystem, Transform worldTransform, float yOffset = 0f)
		{
			if (particleSystem == null)
			{
				return null;
			}
			GameObject gameObject = DynamicObjectPool.Instance.Create(particleSystem.gameObject);
			if (gameObject == null)
			{
				return null;
			}
			ParticleSystem component = gameObject.GetComponent<ParticleSystem>();
			if (light != 0)
			{
				float num = Mathf.Max((float)(int)light / 300f, 0.5f);
				Material particleMaterial = BaseWorldController.VoxelPlayerData.VoxelWorldData.ParticleMaterial;
				particleMaterial.color = new Color(num, num, num);
				component.GetComponent<Renderer>().material = particleMaterial;
			}
			Vector3 position = worldTransform.TransformPoint((float)block._wx + 0.5f, (float)block._wy + 0.5f + yOffset, (float)block._wz + 0.5f);
			component.transform.position = position;
			component.transform.localScale = worldTransform.localScale;
			component.Stop();
			component.Play();
			return component;
		}
	}
}
